using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.ServiceModel;
using System.Threading;
using AutoMapper;
using Core;
using IglooCoder.Commons.WcfNhibernate;
using Infrastucture;
using Omu.ValueInjecter;
using WcfService1.DTO;

namespace WcfService1
{
    // NOTE: You can use the "Rename" command on the "Refactor" SalesInvoice to change the class name "SalesInvoiceServices" in code, svc and config file together.
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    [NHibernateContext]
    public class SalesInvoiceServices : ISalesInvoiceServices
    {
        private readonly SalesInvoiceRepository SalesInvoiceRepository;
        private readonly DeliveryOrderRepository _DORepository;
        private readonly CashBankInDetailRepository _cashBankInRepository;
        private readonly CoaRepository _coaRepository;
        private readonly WcfSessionStorage _session;
        private readonly HppRepository _hppRepository;
        private readonly JurnalRepository _jurnalRepository;
        private readonly MutasiPiutangRepository _mutasiPiutangRepo;

        public SalesInvoiceServices()
        {
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
            _session = new WcfSessionStorage();
            SalesInvoiceRepository = new SalesInvoiceRepository(_session);
            _DORepository = new DeliveryOrderRepository(_session);
            _cashBankInRepository = new CashBankInDetailRepository(_session);
            _coaRepository = new CoaRepository(_session);
            _hppRepository = new HppRepository(_session);
            _jurnalRepository = new JurnalRepository(_session);
            _mutasiPiutangRepo = new MutasiPiutangRepository(_session);
        }

        #region ISalesInvoiceServices Members

        public List<SalesInvoiceDTO> GetList(int page, out long recordCount, string sortProperty = null,
                                             string sortType = "ASC")
        {
           
            var Results = new List<SalesInvoiceDTO>();
            if (string.IsNullOrEmpty(sortProperty))
            {
                Results = SalesInvoiceRepository.GetDataPaged(page, 30, "InvoiceDate", "DESC").ToList();
            }
            else
            {
                Results = SalesInvoiceRepository.GetDataPaged(page, 30, sortProperty, sortType).ToList();
            }
            recordCount = SalesInvoiceRepository.RecordCount;
            foreach (var salesInvoiceDto in Results)
            {
                salesInvoiceDto.TotalRp = salesInvoiceDto.Total*salesInvoiceDto.Rate;
            }

            return Results;
        }

        public bool Save(SalesInvoiceDTO SalesInvoiceDTO,bool isNew)
        {
            bool res = false;
            try
            {
                SalesInvoiceRepository.ExecuteTransactionNative(()=>
                                                                    {

                                                                       
                                                                        SalesInvoice SalesInvoice = SalesInvoiceRepository.GetDataOne(x => x.SINo == SalesInvoiceDTO.SINo);

                                                                        if (SalesInvoice == null)
                                                                        {
                                                                            SalesInvoice = new SalesInvoice();
                                                                            Mapper.Map(SalesInvoiceDTO, SalesInvoice);

                                                                            //Untuk New entry dari transaksi bukan dari email
                                                                            if (String.IsNullOrEmpty(SalesInvoice.SINo))
                                                                                SalesInvoice.SINo = GetLastSINo(SalesInvoiceDTO.CostCenter);

                                                                            foreach (var siDetail in SalesInvoice.SalesInvoiceDetails)
                                                                            {
                                                                                siDetail.SalesInvoice = SalesInvoice;
                                                                                siDetail.SalesInvoiceDetailId = 0;
                                                                            }
                                                                            SalesInvoiceRepository.SaveOrUpdate(SalesInvoice);

                                                                            DeliveryOrder deliveryOrder = _DORepository.FetchById(SalesInvoice.DeliveryOrder.DONo);
                                                                            deliveryOrder.Status = Convert.ToInt16(Status.Close);
                                                                            _DORepository.SaveOrUpdate(deliveryOrder);
                                                                        }
                                                                        else
                                                                        {
                                                                            if (isNew)throw new Exception("NoInvoice sudah ada!");
                                                                            //Jika Status Void dan Close tdk boleh Update
                                                                            if (SalesInvoice.Status == (int)Status.Void || SalesInvoice.Status == (int)Status.Close)
                                                                                throw new Exception("Tidak bisa di Update Status sudah Close atau Void!");
                                                                            SalesInvoiceRepository.Delete(SalesInvoice);
                                                                            var SI = new SalesInvoice();
                                                                            SI.modified_date = DateTime.Now;
                                                                            SI.created_date = SalesInvoice.created_date;
                                                                            Mapper.Map(SalesInvoiceDTO, SI);
                                                                            foreach (var siDetail in SI.SalesInvoiceDetails)
                                                                                siDetail.SalesInvoice = SI;
                                                                            SalesInvoiceRepository.Session.SaveOrUpdateCopy(SI);

                                                                            DeliveryOrder deliveryOrder = _DORepository.FetchById(SI.DeliveryOrder.DONo);
                                                                            deliveryOrder.Status = Convert.ToInt16(Status.Close);
                                                                            _DORepository.SaveOrUpdate(deliveryOrder);
                                                                        }


                                                                        res = true;               
                                                                    });

              
            }
            catch (Exception)
            {
                res = false;
                throw;
            }

            return res;
        }


        public bool Delete(SalesInvoiceDTO SalesInvoiceDto)
        {
            bool res;
            try
            {
                SalesInvoice SalesInvoice = SalesInvoiceRepository.GetDataOne(x => x.SINo == SalesInvoiceDto.SINo);
                //SalesInvoice.Status = 2;
                //SalesInvoiceRepository.SaveOrUpdate(SalesInvoice);

                SalesInvoiceRepository.ExecuteTransactionNative(() =>
                                                                    {
                                                                        SalesInvoiceRepository.Delete(SalesInvoice);
                                                                        var deliveryOrder =
                                                                            _DORepository.GetDataOne(
                                                                                x => x.DONo == SalesInvoice.DeliveryOrder.DONo);
                                                                        deliveryOrder.Status = 0;
                                                                        _DORepository.Update(deliveryOrder);
                                                                    });
                res = true;
            }
            catch (Exception)
            {
                res = false;
            }

            return res;
        }


        public List<SalesInvoiceDTO> GetListByCriteria(Dictionary<String, Object> criteria, string expression, int page,
                                                       out long recordCount)
        {
            var Results = new List<SalesInvoiceDTO>();
            Results = SalesInvoiceRepository.GetDataPaged(criteria, expression, page, 30).ToList();
            recordCount = SalesInvoiceRepository.RecordCount;
            //Results = (from s in SalesInvoices
            //           select new SalesInvoiceDTO
            //           {
            //               SINo = s.SINo,
            //               InvoiceDate = s.InvoiceDate,
            //               CustomerName = s.CustomerName,
            //               Currency = s.Currency.ConvertToDTO<Currency, CurrencyDTO>(),
            //               SONo = s.SONo,
            //               Status = (Status)s.Status,
            //               DONo = s.DONo,
            //               PSNo = s.PSNo,
            //               Rate = s.Rate,
            //               Term = s.Term,
            //               Ppn = s.Ppn,
            //               Keterangan = s.Keterangan,
            //               Discount = s.Discount,
            //               DiscountType = s.DiscountType,
            //               NamaDepan = s.NamaDepan
            //           }).ToList();

            return Results;
        }


        public List<SalesInvoiceDTO> GetAllSalesInvoice()
        {
            IList<SalesInvoice> SalesInvoices = SalesInvoiceRepository.FetchAll();
            IEnumerable<SalesInvoiceDTO> Results = from s in SalesInvoices
                                                   let source = s.Customer
                                                   where source != null
                                                   select new SalesInvoiceDTO {SINo = s.SINo,InvoiceDate = s.InvoiceDate,Customer = (CustomerDTO) new CustomerDTO().InjectFrom(source)};
            return Results.ToList();
        }


        public SalesInvoiceDTO GetSalesInvoice(String kodeSalesInvoice)
        {
            SalesInvoice SalesInvoice = SalesInvoiceRepository.GetDataOne(x => x.SINo == kodeSalesInvoice);
            var salesInvoiceDto = new SalesInvoiceDTO();
            salesInvoiceDto.InjectFrom(SalesInvoice);
            if (SalesInvoice.CostCenter != null)
                salesInvoiceDto.CostCenter = (CostCenterDTO) new CostCenterDTO().InjectFrom(SalesInvoice.CostCenter);
            if (SalesInvoice.Customer != null)
                salesInvoiceDto.Customer = (CustomerDTO) new CustomerDTO().InjectFrom(SalesInvoice.Customer);
            if (SalesInvoice.Currency != null)
                salesInvoiceDto.Currency = (CurrencyDTO) new CurrencyDTO().InjectFrom(SalesInvoice.Currency);
            if (SalesInvoice.Salesman != null)
                salesInvoiceDto.Salesman = (EmployeeDTO)new EmployeeDTO().InjectFrom(SalesInvoice.Salesman);
            if (SalesInvoice.DeliveryOrder !=null)
                salesInvoiceDto.DeliveryOrder = (DeliveryOrderDTO) new DeliveryOrderDTO().InjectFrom(SalesInvoice.DeliveryOrder);
            
            salesInvoiceDto.SalesInvoiceDetails = new ObservableCollection<SalesInvoiceDetailDTO>();
            foreach (var salesInvoiceDetail in SalesInvoice.SalesInvoiceDetails)
            {
                var siDetail = new SalesInvoiceDetailDTO();
                siDetail.InjectFrom(salesInvoiceDetail);
                if (salesInvoiceDetail.Inventory != null)
                {
                    siDetail.Inventory = (InventoryDTO) new InventoryDTO().InjectFrom(salesInvoiceDetail.Inventory);
                    siDetail.Inventory.Item = (ItemDTO) new ItemDTO().InjectFrom(salesInvoiceDetail.Inventory.Item);
                    siDetail.Inventory.SatuanUnit = (SatuanDTO) new SatuanDTO().InjectFrom(salesInvoiceDetail.Inventory.SatuanUnit);
                }
                salesInvoiceDto.SalesInvoiceDetails.Add(siDetail);
            }
            return salesInvoiceDto;
        }

        private void Map(SalesInvoice source,SalesInvoiceDTO destination)
        {
            
        }

        public List<SalesInvoiceDTO> GetSalesInvoiceList(string kodeSalesInvoice)
        {
            List<SalesInvoice> SalesInvoices =
                SalesInvoiceRepository.GetDataList(x => x.SINo.Contains(kodeSalesInvoice)).ToList();
            var Results = new List<SalesInvoiceDTO>();
            SalesInvoices.ForEach(x =>
                                      {
                                          var salesInvoiceDto = new SalesInvoiceDTO();
                                          salesInvoiceDto.InjectFrom(x);
                                          if (x.CostCenter != null)
                                              salesInvoiceDto.CostCenter = (CostCenterDTO)new CostCenterDTO().InjectFrom(x.CostCenter);
                                          if (x.Customer != null)
                                              salesInvoiceDto.Customer = (CustomerDTO)new CustomerDTO().InjectFrom(x.Customer);
                                          if (x.Currency != null)
                                              salesInvoiceDto.Currency = (CurrencyDTO)new CurrencyDTO().InjectFrom(x.Currency);
                                          if (x.Salesman != null)
                                              salesInvoiceDto.Salesman = (EmployeeDTO)new EmployeeDTO().InjectFrom(x.Salesman);
                                          if (x.DeliveryOrder != null)
                                              salesInvoiceDto.DeliveryOrder = (DeliveryOrderDTO)new DeliveryOrderDTO().InjectFrom(x.DeliveryOrder);

                                          salesInvoiceDto.SalesInvoiceDetails = new ObservableCollection<SalesInvoiceDetailDTO>();
                                          foreach (var salesInvoiceDetail in x.SalesInvoiceDetails)
                                          {
                                              var siDetail = new SalesInvoiceDetailDTO();
                                              siDetail.InjectFrom(salesInvoiceDetail);
                                              if (salesInvoiceDetail.Inventory != null)
                                              {
                                                  siDetail.Inventory = (InventoryDTO)new InventoryDTO().InjectFrom(salesInvoiceDetail.Inventory);
                                                  siDetail.Inventory.Item = (ItemDTO)new ItemDTO().InjectFrom(salesInvoiceDetail.Inventory.Item);
                                              }
                                              salesInvoiceDto.SalesInvoiceDetails.Add(siDetail);
                                          }
                                          Results.Add(salesInvoiceDto);
                                      }
                );

            return Results;
        }

        public string GetLastSINo(CostCenterDTO costCenter)
        {
            var appParamServices = new AppParamServices();
            AppParamDTO param = appParamServices.GetAppParamList("KodeSI").FirstOrDefault();
            string res = SalesInvoiceRepository.GetMaxNumber(costCenter.CostCenterId,param.AppParamValue);
            if (string.IsNullOrEmpty(res))
                res = param.AppParamValue;

            string formatSO = "0000";
            string lastNo = string.Empty;
            string kodeSO;
            if (res.Length <= 4)
            {
                lastNo = formatSO;
                kodeSO = res;
            }
            else
            {
                lastNo = res.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries)[3];
                kodeSO = res.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries)[0];
            }
            string soNum = formatSO.Substring(0, formatSO.Length - (int.Parse(lastNo) + 1).ToString().Length) +
                           (int.Parse(lastNo) + 1);
            return kodeSO + "/" + costCenter.CostCenterId + "/" + DateTime.Now.Year + "/" + soNum;
        }

        public bool DeleteDetail(SalesInvoiceDetailDTO SalesInvoiceDetailDto)
        {
            bool res;
            try
            {
                var SIDetail =
                    SalesInvoiceRepository.Session.Get<SalesInvoiceDetail>(SalesInvoiceDetailDto.SalesInvoiceDetailId);
                SalesInvoiceRepository.DeleteDetail(SIDetail, SalesInvoiceDetailDto.SalesInvoiceDetailId);
                res = true;
            }
            catch (Exception)
            {
                res = false;
            }

            return res;
        }

        public void UpdateStatus(SalesInvoiceDTO salesInvoiceDto, Status status)
        {
            SalesInvoiceRepository.UpdateStatus(salesInvoiceDto.SINo, (int)status);
        }

        public List<SalesInvoiceDTO> GetSalesInvoiceListByCostCenter(CostCenterDTO costCenter)
        {
            List<SalesInvoice> SalesInvoices =
                SalesInvoiceRepository.GetDataList(x => x.CostCenter.CostCenterId == costCenter.CostCenterId).OrderBy(x=>x.SINo).ToList();
            IEnumerable<SalesInvoiceDTO> Results = from s in SalesInvoices
                                                   select new SalesInvoiceDTO { SINo = s.SINo };
            return Results.ToList();
        }


        public IEnumerable<SalesInvoiceDTO> GetSalesInvoiceByCustomer(CustomerDTO customer)
        {
            List<MutasiPiutang> piutangList = new List<MutasiPiutang>();
            if (customer != null)
                piutangList = _mutasiPiutangRepo.GetPiutangBelumLunas(customer.CustomerId);
            else
                piutangList = _mutasiPiutangRepo.GetPiutangBelumLunas();


            var results = new List<SalesInvoiceDTO>();
            foreach (MutasiPiutang x in piutangList)
            {
                var si = new SalesInvoiceDTO();
                si.SINo = x.InvoiceNo;
                si.Total = x.SaldoSisa;
                results.Add(si);
            }
            return results;
        }


        public void CreateJurnal(SalesInvoice salesInvoice)
        {
            //Jurnal Piutang
            Jurnal jurnal = new Jurnal();
            jurnal.NoVoucher = salesInvoice.SINo;
            jurnal.TglJurnal = salesInvoice.InvoiceDate;
            jurnal.Description = salesInvoice.Keterangan;
            jurnal.ReffNumber = salesInvoice.SINo;
            jurnal.TipeJurnal = typeof (SalesInvoice).Name;

            
            decimal afterDisc = 0;
            if (salesInvoice.DiscountType == "%")
                afterDisc = salesInvoice.SalesInvoiceDetails.Sum(x => x.TotalDetail) -
                            (salesInvoice.SalesInvoiceDetails.Sum(x => x.TotalDetail)*salesInvoice.Discount/100);
            else
                afterDisc = salesInvoice.SalesInvoiceDetails.Sum(x => x.TotalDetail) - salesInvoice.Discount;

            foreach (var invoiceDetail in salesInvoice.SalesInvoiceDetails)
            {
                //Penjualan
                if (string.IsNullOrEmpty(invoiceDetail.Inventory.Item.CoaSales)) throw new Exception(invoiceDetail.Inventory.SerialNumber + " belum memiliki chart of account Sales!");
                jurnal.JurnalDetails.Add(new JurnalDetail() { AccountNumber = invoiceDetail.Inventory.Item.CoaSales, Departemen = salesInvoice.CostCenter.Departemen, Kredit = invoiceDetail.TotalDetail, Jurnal = jurnal, Description = invoiceDetail.Inventory.Item.ItemId + ":" + invoiceDetail.Inventory.Item.CoaSales });
                //hpp
                var hpp = _hppRepository.FindLastHpp(invoiceDetail.Inventory.SerialNumber, salesInvoice.InvoiceDate);
                if (hpp.QtyAkhir == 0) throw new Exception(invoiceDetail.Inventory.SerialNumber + " belum ada hppnya!");
                if (string.IsNullOrEmpty(invoiceDetail.Inventory.Item.CoaHpp)) throw new Exception(invoiceDetail.Inventory.SerialNumber + " belum memiliki chart of account Hpp!");
                jurnal.JurnalDetails.Add(new JurnalDetail() { AccountNumber = invoiceDetail.Inventory.Item.CoaHpp, Departemen = salesInvoice.CostCenter.Departemen, Debit = hpp.HppAkhir * invoiceDetail.Qty, Jurnal = jurnal, Description = invoiceDetail.Inventory.Item.ItemId + ":" + invoiceDetail.Inventory.Item.CoaHpp });
                //inventory
                if (string.IsNullOrEmpty(invoiceDetail.Inventory.Item.CoaInventory)) throw new Exception(invoiceDetail.Inventory.SerialNumber + " belum memiliki chart of account inventory!");
                jurnal.JurnalDetails.Add(new JurnalDetail() { AccountNumber = invoiceDetail.Inventory.Item.CoaInventory, Departemen = salesInvoice.CostCenter.Departemen, Kredit = hpp.HppAkhir * invoiceDetail.Qty, Jurnal = jurnal, Description = invoiceDetail.Inventory.Item.ItemId + ":" + invoiceDetail.Inventory.Item.CoaInventory });
            }

            List<JurnalDetail> results = new List<JurnalDetail>();
            //Piutang 
            if (salesInvoice.Customer.Coa == null) throw new Exception(salesInvoice.Customer.CustomerName + " belum memiliki chart of account!");
            results.Add(new JurnalDetail() { AccountNumber = salesInvoice.Customer.Coa.AccountNumber, Departemen = salesInvoice.CostCenter.Departemen, Debit = salesInvoice.Ppn == 0 ? afterDisc : afterDisc + salesInvoice.Ppn, Jurnal = jurnal });
          
            foreach (var gjd in jurnal.JurnalDetails.GroupBy(x => x.Description))
            {
                results.Add(new JurnalDetail()
                {
                    AccountNumber = jurnal.JurnalDetails.FirstOrDefault(x=> x.Description == gjd.Key).AccountNumber,
                    Debit = jurnal.JurnalDetails.Where(x => x.Description == gjd.Key).Sum(x=>x.Debit),
                    Kredit = jurnal.JurnalDetails.Where(x => x.Description == gjd.Key).Sum(x => x.Kredit),
                    Departemen = jurnal.JurnalDetails.FirstOrDefault(x => x.Description == gjd.Key).Departemen,
                    Description = jurnal.JurnalDetails.FirstOrDefault(x => x.Description == gjd.Key).Description,
                    Jurnal = jurnal
                });
            }
            if (salesInvoice.Ppn > 0 )
            {
                var coaPPn = _coaRepository.GetDataOne(x => x.AccountName.Contains("ppn"));
                if (coaPPn == null)throw  new Exception("Chart of Account PPN belum ada!");
                results.Add(new JurnalDetail() { AccountNumber = coaPPn.AccountNumber, Departemen = salesInvoice.CostCenter.Departemen, Kredit = salesInvoice.Ppn, Jurnal = jurnal });
            }
            jurnal.JurnalDetails = results;
            _jurnalRepository.SaveOrUpdate(jurnal);

            //Mutasi HPP
            var hppServices = new HppServices();
            hppServices.CreateHpp(salesInvoice);


            //Kartu Piutang
            var mutasiPiutangRepo = new MutasiPiutangRepository(_session);
            var lastmutasi = mutasiPiutangRepo.FindLastMutasi(salesInvoice.Customer.CustomerId, salesInvoice.SINo,
                                                              salesInvoice.InvoiceDate);
            var mutasiPiutang = new MutasiPiutang();
            mutasiPiutang.SaldoAwal = lastmutasi.SaldoSisa;
            mutasiPiutang.SaldoMasuk = salesInvoice.Ppn == 0 ? afterDisc : afterDisc + salesInvoice.Ppn;
            mutasiPiutang.SaldoSisa = mutasiPiutang.SaldoAwal + mutasiPiutang.SaldoMasuk;
            mutasiPiutang.CustomerId = salesInvoice.Customer.CustomerId;
            mutasiPiutang.Tanggal = salesInvoice.InvoiceDate;
            mutasiPiutang.Type = typeof (SalesInvoice).Name;
            mutasiPiutang.InvoiceNo = salesInvoice.SINo;
            mutasiPiutang.Coa = salesInvoice.Customer.Coa.AccountNumber;
            mutasiPiutang.ReferenceNumber = salesInvoice.SINo;
            mutasiPiutangRepo.SaveOrUpdate(mutasiPiutang);
        
        }


        public void Posting(IEnumerable<SalesInvoiceDTO> salesInvoiceDtos)
        {
            SalesInvoiceRepository.ExecuteTransactionNative(() =>
            {
                foreach (var salesInvoiceDto in salesInvoiceDtos)
                {
                    var si = SalesInvoiceRepository.GetDataOne(x => x.SINo == salesInvoiceDto.SINo);
                    SalesInvoiceRepository.UpdateStatus(salesInvoiceDto.SINo, 3);
                    CreateJurnal(si);
                }
            });

        }


        public void UnPosting(IEnumerable<SalesInvoiceDTO> salesInvoiceDtos)
        {
            
            SalesInvoiceRepository.ExecuteTransactionNative(() =>
            {
                foreach (var salesInvoiceDto in salesInvoiceDtos)
                {
                    var salesInvoice = SalesInvoiceRepository.GetDataOne(x => x.SINo == salesInvoiceDto.SINo);
                    SalesInvoiceRepository.UpdateStatus(salesInvoice.SINo, 1);
                    var jurnal = _jurnalRepository.GetDataOne(x => x.NoVoucher == salesInvoiceDto.SINo);
                    _jurnalRepository.Delete(jurnal);
                    _hppRepository.DeleteHpp(salesInvoiceDto.SINo);
                    var piutangReposioty = new MutasiPiutangRepository(_session);
                    piutangReposioty.DeleteMutasiPiutang(salesInvoiceDto.SINo);
                }
            });

        }


           public IEnumerable<SalesInvoiceDTO> GetListByPeriode(DateTime tglstart, DateTime tglend)
        {
            List<SalesInvoice> doElses = SalesInvoiceRepository.GetDataList(x => x.InvoiceDate >= tglstart && x.InvoiceDate <= tglend).ToList();
            var results = new List<SalesInvoiceDTO>();

            foreach (var doElse in doElses)
            {
                var doElseDTO = new SalesInvoiceDTO();
                doElseDTO.InjectFrom(doElse);
                results.Add(doElseDTO);
            }

            return results;
        }

        #endregion
    }
}